package com.gitee.feizns.bean;

import com.gitee.feizns.convert.ConvertUtils;
import com.gitee.feizns.reflect.MethodUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Stream;

/**
 * @author feizns
 * @since 2019/6/9 0009
 */
class DefaultPropertyImpl<D> implements Property<D> {

    /**
     * 字段
     */
    private Field field;

    /**
     *
     */
    private Object target;

    /**
     * 读方法
     */
    private Method readMethod;

    /**
     * 写方法
     */
    private Method writeMethod;

    /**
     *
     */
    private String name;

    public DefaultPropertyImpl(Object target, String propertyName) {
        this.name = propertyName;
        if ( target != null ) {
            this.target = target;
            Class<?> targetType = target.getClass();
            this.readMethod = PropertyUtils.getReadMethod(targetType, propertyName);
            if ( this.readMethod != null )
                this.writeMethod = PropertyUtils.getWriteMethod(targetType, propertyName, this.readMethod.getReturnType());
            this.field = PropertyUtils.getField(targetType, propertyName);
        }
    }

    @Override
    public D val() {
        return readMethod != null ? (D) MethodUtils.invoke(target, readMethod) : null;
    }

    @Override
    public <R> R val(Class<R> targetType) {
        return ConvertUtils.to(val(), targetType);
    }

    @Override
    public D getAndSet(D newVal) {
        D oldVal = val();
        set(newVal);
        return oldVal;
    }

    @Override
    public Property<D> set(Object newVal) {
        if ( writeMethod != null ) {
            Object realNewVal = ConvertUtils.to(newVal, writeMethod.getParameterTypes()[0]);
            MethodUtils.invoke(target, writeMethod, realNewVal);
        }
        return this;
    }

    @Override
    public String name() {
        return name;
    }

    @Override
    public boolean isReadable() {
        return readMethod != null;
    }

    @Override
    public boolean isWritable() {
        return writeMethod != null;
    }

    @Override
    public Object getTarget() {
        return target;
    }

    @Override
    public Field getField() {
        return field;
    }

    @Override
    public Method readMethod() {
        return readMethod;
    }

    @Override
    public Method writeMethod() {
        return writeMethod;
    }

    //字段优先.
    @Override
    public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
        Stream<AnnotatedElement> elements = annotatedElements().filter(item -> item.isAnnotationPresent(annotationClass));
        Optional<AnnotatedElement> annotatedElement = elements.findFirst();
        return annotatedElement.isPresent() ? annotatedElement.get().getAnnotation(annotationClass) : null;
    }

    @Override
    public Annotation[] getAnnotations() {
        return annotatedElements().flatMap(item -> Arrays.stream(item.getAnnotations())).toArray(Annotation[]::new);
    }

    @Override
    public Annotation[] getDeclaredAnnotations() {
        return annotatedElements().flatMap(item -> Arrays.stream(item.getDeclaredAnnotations())).toArray(Annotation[]::new);
    }

    //字段优先
    private Stream<AnnotatedElement> annotatedElements() {
        return Arrays.stream(new AnnotatedElement[]{ field, readMethod, writeMethod }).filter(Objects::nonNull);
    }

    @Override
    public String toString() {
        StringBuilder ret = new StringBuilder();
        ret.append("name = ").append(name).append(", ");
        ret.append("field = ").append(field).append(", ");
        ret.append("target = ").append(target).append(", ");
        ret.append("readMethod = ").append(readMethod).append(", ");
        ret.append("writeMethod = ").append(writeMethod).append(", ");
        return ret.toString();
    }
}
